package structure

import (
	"fmt"
	"math"

	"gddgame.cc/galaxy/utils"
	"gddgame.cc/galaxy/utils/uuid"
)

type TimeLinkStruct struct {
}

// Link:多对多，无顺序，支持批量读取，不可重复
// time: 为link加入时效性，提前加入
func (execute *Execute) GetTimeLink(conn SafeConn, data Data, relation string, min int64, max int64) []BaseData {
	key := execute.Key + "/" + execute.GetDataType(data.GetDataType()) + utils.ToString(data.GetId()) + "/" + relation

	return execute.getTimeLinks(conn, key, min, max)
}

func (execute *Execute) GetTimeLinksWithRelation(conn SafeConn, t PublicDataType, relationIds map[string][]interface{}, min int64, max int64) []BaseData {
	all := []interface{}{}
	for relation, ids := range relationIds {
		keys := make([]interface{}, len(ids))
		for i, id := range ids {
			keys[i] = execute.Key + "/" + execute.GetDataType(t) + utils.ToString(id) + "/" + relation
		}
		all = append(all, keys...)
	}
	key := execute.Key + "/" + execute.GetDataType(t) + uuid.RandomString()
	execute.mergeTimeLinks(conn, key, all)
	return execute.getTimeLinks(conn, key, min, max)
}

func (execute *Execute) GetTimeLinksWithIds(conn SafeConn, t PublicDataType, ids []interface{}, relations []string, min int64, max int64) []BaseData {
	all := make([]interface{}, len(ids)*len(relations))
	for j, relation := range relations {
		for i, id := range ids {
			all[len(ids)*j+i] = execute.Key + "/" + execute.GetDataType(t) + utils.ToString(id) + "/" + relation
		}
	}
	key := execute.Key + "/" + execute.GetDataType(t) + uuid.RandomString()
	execute.mergeTimeLinks(conn, key, all)
	return execute.getTimeLinks(conn, key, min, max)
}

func (execute *Execute) mergeTimeLinks(conn SafeConn, key interface{}, keys []interface{}) {
	args := make([]interface{}, 0, len(keys)+2)
	args = append(args, key, len(keys))
	args = append(args, keys...)
	if r, err := conn.Do("ZUNIONSTORE", args...); err != nil {
		fmt.Println("link time merge err:", err)
	} else {
		fmt.Println("link time merge:", r)
	}

	if err := conn.Send("EXPIRE", key, 60); err != nil {
		fmt.Println("EXPIRE", err)
	}
}

func (execute *Execute) getTimeLinks(conn SafeConn, key interface{}, min int64, max int64) []BaseData {
	var minKey interface{}
	var maxKey interface{}
	if min == math.MinInt64 {
		minKey = "-inf"
	} else {
		minKey = min
	}
	if max == math.MaxInt64 {
		maxKey = "+inf"
	} else {
		maxKey = max
	}
	//lrange 0,-1
	//fmt.Println("ZRANGEBYSCORE", key, minKey, maxKey)
	c, _ := conn.Do("ZRANGEBYSCORE", key, minKey, maxKey)
	if c == nil {
		return nil
	}
	rl := make([]BaseData, len(c.([]interface{})))
	for index, v := range c.([]interface{}) {
		r := BaseData{}
		_, _ = r.UnmarshalMsg(v.([]byte))
		rl[index] = r
	}
	return rl
}

func (execute *Execute) AddTimeLink(conn SafeConn, data1 Data, data2 Data, relation string, time int64) {
	key := execute.Key + "/" + execute.GetDataType(data1.GetDataType()) + utils.ToString(data1.GetId())
	//conn.Watch(key)
	_ = conn.Send("ZADD", key+"/"+relation, time, data2.Bytes())
	_ = conn.Send("HSET", key, "link/"+relation, 1)

	key = execute.Key + "/" + execute.GetDataType(data2.GetDataType()) + utils.ToString(data2.GetId())
	//conn.Watch(key)
	_ = conn.Send("ZADD", key+"/"+relation, time, data1.Bytes())
	_ = conn.Send("HSET", key, "link/"+relation, 1)
}

func (execute *Execute) RemoveTimeLink(conn SafeConn, data1 Data, data2 Data, relation string) {
	key := execute.Key + "/" + execute.GetDataType(data1.GetDataType()) + utils.ToString(data1.GetId()) + "/" + relation
	_, _ = conn.Do("ZREM", key, data2.Bytes())

	key = execute.Key + "/" + execute.GetDataType(data2.GetDataType()) + utils.ToString(data1.GetId()) + "/" + relation
	_, _ = conn.Do("ZREM", key, data1.Bytes())
}

func (execute *Execute) ClearTimeLink(conn SafeConn, data Data, relation string) {
	rl := execute.GetLink(conn, data, relation)
	if rl == nil || len(rl) == 0 {
		return
	}

	for _, v := range rl {
		key := execute.Key + "/" + execute.GetDataType(v.DataType) + utils.ToString(v.Id) + "/" + relation
		_, _ = conn.Do("ZREM", key, 0, data.Bytes())
	}
	_ = conn.Send("DEL", execute.Key+"/"+execute.GetDataType(data.GetDataType())+utils.ToString(data.GetId())+"/"+relation)
	_ = conn.Send("HSET", execute.Key+"/"+execute.GetDataType(data.GetDataType())+utils.ToString(data.GetId()), "link/"+relation, 0)
}
